#ifdef __GNUG__
#pragma implementation
#endif

#include "Object.h"

#include "Class.h"
#include "OrdColl.h"
#include "IdDictionary.h"
#include "ObjInt.h"
#include "Error.h"
#include "String.h"
#include "Storage.h"
#include "ProgEnv.h"
#include "System.h"
#include "ObjectTable.h"
#include "AccessMem.h"
#include "MemBuf.h"
#include "CmdNo.h"
#include "ET_stdio.h"

//---- observing ---------------------------------------------------------

static IdDictionary *Observer;
// dictionary associating a list of observers with an Object

static IdDictionary *DelayedChanges;
// dictionary to store the delayed Change messages, the key is the object pointer
// (IdDictionary) the value is an OrderedCollection of ChangeMessages (see below).
// The first entry of the OrderedCollection is a reference count of the number of
// DelayChanges requests. FlushChanges will be executed when the count drops to 0.
// Whether change messages are delayed is set with the cObjDelayChanges flag

ONEXIT(Object)
{
	if (Observer) {
		Observer->FreeValues();
		SafeDelete(Observer);
	}
	if (DelayedChanges) {
		DelayedChanges->FreeAllValues();
		SafeDelete(DelayedChanges);
	}
}

class ChangeMessage: public Object {
public:
	void *what;
	int part, id;
public:
	ChangeMessage(void *w, int p, int i)
		{ what= w; part= p; id= i; }
};

extern u_long Storage_lastobj, Storage_lastobjend;
extern bool Storage_proto;

//---- Object ------------------------------------------------------------------

int Object::_an;
AccessMembers *Object::_accessMembers;

//---- automatically added by macro metaImpl -----------------------------------

Class *Object::isa;

static Class ObjectClassMetaImpl0("Object",
				  sizeof(Object),
				  (Class*) 0,
				  new Object((_Object_dummy*)0),
				  __FILE__,
				  "./Object.h",
				  __LINE__,
				  1,
				  TRUE,
				  (double)0.0);

Object::Object(_Object_dummy*)
{
	Storage_proto= TRUE;
	flags_= cObjNonDeleted | cObjIsProto;
	if ((u_long)this >= Storage_lastobj && (u_long)this < Storage_lastobjend)
		SetFlag(cObjIsShared);
	isa= &ObjectClassMetaImpl0;
	//ObjectClassMetaImpl0.SetSuper();
}

Class *Object::IsA() const
{
	return &ObjectClassMetaImpl0;
}

void Object::Members(AccessMembers *ac)
{
	_accessMembers= ac;
	ScanMembers(TX(flags_), TP(Observer), TP(DelayedChanges), TP(isa));
}

//---- ordinary methods ----------------------------------------------------

Object::Object(int f)
{
	flags_= (f & cFlagMask) | cObjNonDeleted;
	if ((u_long)this >= Storage_lastobj && (u_long)this < Storage_lastobjend) {
		SetFlag(cObjIsShared);
		if (Storage_proto)
			flags_|= cObjIsProto;
	}
	ObjectTable::Add(this);
}

Object::Object(const Object &o)
{
	fprintf(stderr, "Object::Object(const Object &o)\n");
	flags_= o.flags_;
	if ((u_long)this >= Storage_lastobj && (u_long)this < Storage_lastobjend) {
		SetFlag(cObjIsShared);
		if (Storage_proto)
			flags_|= cObjIsProto;
	} else
		ResetFlag(cObjIsShared);
	ObjectTable::Add(this);
}

Object::~Object()
{
	if (! TestFlag(cObjNonDeleted))
		Fatal("~Object", "object deleted twice");
		
	if (IsObserved()) {
		Collection *observers= GetObservers(); // GetObservers is statically bound!!!
		if (observers)
			CleanupObservers(observers);
		flags_ &= ~cObjIsObserved;
	}
	flags_&= ~cObjNonDeleted;
	ObjectTable::Remove(this);
}

void Object::FreeAll()
{
}

//----- flag manipulation ------------------------------------------------------

void Object::SetFlag(int f, bool b)
{
	if (b)
		SetFlag(f);
	else
		ResetFlag(f);
}

//---- attributes --------------------------------------------------------------

const char *Object::ClassName() const
{
	return IsA()->Name();
}

//---- comparing ---------------------------------------------------------------

u_long Object::Hash ()
{
	return (u_long) this;
}

bool Object::IsEqual(Object *anOp)
{
	return anOp == this;
}

int Object::Compare(Object*)
{
	AbstractMethod("Compare");
	return 0;
}

//---- converting, activation/passivation ------------------------------------

const char *Object::AsString()
{
	return 0;
}

static bool printOnWhenObserved(Object*, Object *op, Object *from)
{
	return (ObjectTable::PtrIsValid(op) && op->PrintOnWhenObserving(from));
}

OStream &Object::PrintOn(OStream &os)
{
	Collection *selectedDeps= 0;
		
	os.MakeIndex(this);
	if (IsObserved()) {
		Collection *observers= GetObservers();
		if (observers->IsEmpty()) { // impossible ??
			flags_ &= ~cObjIsObserved;
			DestroyObserverColl();
		} else {
			selectedDeps= observers->Select((BoolFun)printOnWhenObserved, this);
			if (selectedDeps->IsEmpty())
				SafeDelete(selectedDeps);
		}
	}
	os << (flags_ & cFlagMask) SP << selectedDeps SP;
	SafeDelete(selectedDeps);
	return os;
}

OStream &operator<< (OStream &os, Object *op)
{
	int ix;

	if ((op == 0) || (op == op->IsA())) {       // a null pointer
		os.put('0');                            // "0" seems to be portable!

	} else if ((ix= os.IndexOfPtr(op)) >= 0 ) { // already in table ?
		os.put('@');
		os << ix;                               // write index
		
	} else {
		Object *op2;
		// special case: we are copying inside a single address space
		// try if class has overwritten deepclone
		if (os.IsForDeepClone() && (op2= op->deepclone())) {
			os.put('#');
			os << os.MakeIndex(op) SP << (u_long) op2;
		} else {
			if (! op->IsKindOf(Class))
				os.put('\n');                   // pretty printing
			os.put('{');
			os << os.MakeIndex(op) SP << op->IsA();
			op->PrintOn(os);
			os.put('}');
		}
	}
	os.put(' ');
	os.flush();
	return os;
}

IStream &Object::ReadFrom(IStream &is)
{
	int f;
	Collection *observers;

	is.MakeIndex(this);
	is >> f >> observers;
	SetFlag(f);
	if (observers) {
		flags_ |= cObjIsObserved;
		SetObserverColl(observers);
	}
	return is;
}

Object *Object::ReadAndMap(IStream &is)
{
	Object *op= New();
	(*op).ReadFrom(is);
	return op;
}

/* Read in new object, put a pointer to it in *op.
*  Make sure object read in is cl or a descendant of cl.
*/
IStream& LoadPtr(IStream &is, Object *&op, Class *expected)
{
	static char *cLoadError= "Object::LoadPtr";
	Class *clp= 0;
	char c= 0;
	bool first;
	int level, ix= 0;
	u_long ptr= 0;

	op= 0;

	is >> c;

	switch (c) {

	case '0':       // a nil pointer
		return is;
		
	case '#':       // a real address from DeepCopy
		is >> ix >> ptr;
		if (is.IsForDeepClone()) {
			op= (Object*) ptr;
			is.AtPut(ix, op);
		} else
			Error(cLoadError, "shouldn't happen: got address %d", ptr);
		break;

	case '@':       // table index of existing object
		is >> ix;
		op= is.At(ix);
		if (op == (Object*) 0x01) {
			Error(cLoadError, "object is 1", ix);
			op= 0;
		} else if (op == 0)
			Error(cLoadError, "can't find element %d in instancetable", ix);
		break;

	case '{':       // new object
		is >> ix >> clp;
		if (clp == 0)   // assume class "Class"
			clp= Meta(Class);
				
		op= clp->Proto()->ReadAndMap(is);
		is.AtPut(ix, op);

		level= 1;
		first= TRUE;
		while (is.get(c)) {
			if (c == '{') {
				level++;
				continue;
			}
			if (c == '}') {
				level--;
				if (level <= 0)
					break;
				continue;
			}
			if (!Isspace(c)) {
				if (first) {
					Error(cLoadError, "expected \'}\' at end of <%s>; got: (%02x)",
															clp->Name(), c);
					first= FALSE;
				}
			}
		};
		break;

	default:
		fprintf(stderr, "%s: unexpected character 0x%02x\n", cLoadError, c & 0xff);
		break;
	}

	if (op == 0) {
		if (expected) {
			Error(cLoadError, "can't read object; creating dummy instance of %s",
															expected->Name());
			op= expected->Proto()->New();
		}
	} else if (expected) {
		clp= op->IsA();
		if (clp && !clp->isKindOf(expected))
			Error(cLoadError, "trying to read a %s into a %s", clp->Name(),
															expected->Name());
	}

	return is;
}

//---- cloning -----------------------------------------------------------------

Object *Object::New()
{
	Object *op= IsA()->Proto()->Object::Clone();
	op->InitNew();
	return op;
}

void Object::InitNew()
{
}

Object *Object::Clone()
{
	Object *op= (Object*) Storage::ObjectAlloc(IsA()->Size());
	// give the new object the correct vtable in a portable? way
	MemCpy(op, this, IsA()->Size());
	op->flags_ |= cObjNonDeleted | cObjIsShared;
	op->flags_ &= ~cObjIsProto;
	ObjectTable::Add(op);
	return op;
}

Object *Object::deepclone()
{
	return 0;
}

Object *Object::DeepClone()
{
	Object *op;

	if (op= deepclone())
		return op;
		
	MemBuf mb;
	OStream to(&mb);
	IStream from(&mb);

	to.PrepareForDeepClone();
	to << this;
	to.flush();

	mb.SwitchToRead();

	from.PrepareForDeepClone();
	from >> op;

	return op;
}

//---- change propagation, object observing ------------------------------------

Collection *Object::MakeObserverColl()
{
	Collection *cp;
	if (Observer == 0)
		Observer= new IdDictionary(70);
	if (cp= (Collection*) Observer->AtKey(this))
		Warning("MakeObserverColl", "there are already Observers for %s", ClassName());
	else {
		cp= new OrdCollection;
		Observer->PutAtKeyIfAbsent(cp, this);
	}
	return cp;
}

Collection *Object::GetObservers()
{
	if (Observer == 0) {
		Warning("GetObservers", "without SetObserver");
		return 0;
	}
	Collection *cp= (Collection*) Observer->AtKey(this);
	if (cp == 0)
		Warning("GetObservers", "observers == 0 in GetObservers for %s", ClassName());
	return cp;
}

void Object::DestroyObserverColl()
{
#ifdef OLD
	if (Observer) {
		Assoc *ap= (Assoc*) Observer->RemoveKey(this);
		if (ap) {
			Object *observers= ap->Value();
			if (observers) {
				SafeDelete(observers);
			} else
				Warning ("DestroyObserverColl", "0 in DestroyObserverColl() for %s",
																ClassName());
			SafeDelete(ap);
		}
	} else
		Warning("DestroyObserverColl", "Observer == 0");
#endif

	if (Observer) {
		Object *value;
		if (Observer->RemoveKey(this, value)) {
			// could also check for IsObserved
			if (value) {
				SafeDelete(value);      // value is a container
			} else
				Warning("DestroyObserverColl", "0 in DestroyObserverColl() for %s",
						ClassName());
		}
	} else
		Warning("DestroyObserverColl", "Observer == 0");
}

void Object::SetObserverColl(Collection *cp)
{
	DestroyObserverColl();
	if (Observer == 0)
		Observer= new IdDictionary(70);
	Observer->PutAtKeyIfAbsent(cp, this);
}

void Object::CleanupObservers(Collection *observers)
{
	Iter next(GetObserverIter());
	Object *op;
	while(op= next()) {
		if (ObjectTable::PtrIsValid(op)) {
			gCerr << op->ClassName() SP << op->AsString() NL;
			op->DoObserve(cIdNone, cPartSenderDied, 0, this);
		}
	}
//	observers->ForEach(Object,DoObserve)(cIdNone, cPartSenderDied, 0, this);
	DestroyObserverColl();
}

void Object::AddObserver(Object *op)
{
	Collection *observer;

	if (op) {
		if (!IsObserved() || ((observer= GetObservers()) == 0)) {
			flags_ |= cObjIsObserved;
			observer= MakeObserverColl();
		} else {
			if (observer->FindPtr(op)) {
				Warning("AddObserver", "duplicate in observers");
				return;
			}
		}
		observer->Add(op);
	}
}

Object *Object::RemoveObserver(Object *op)
{
	if (this && IsObserved()) {
		Collection *observers= GetObservers();
		if (observers) {
			if (op)
				op= observers->RemovePtr(op);
			if (observers->IsEmpty()) {
				flags_ &= ~cObjIsObserved;
				DestroyObserverColl();
			}
			return op;
		}
	}
	return 0;
}

void Object::Send(int id, int part, void *vp)
{
	if (!IsObserved())
		return;

	if (TestFlag(cObjDelayChanges)) {
		if (DelayedChanges) {
			Collection *col= (Collection*) DelayedChanges->AtKey(this);
			if (col == 0) {
				Warning("Send", "No collection for change messages, %s", ClassName());
				col= new OrdCollection;
				DelayedChanges->PutAtKeyIfAbsent(col, this);
			}
			col->Add(new ChangeMessage(vp, part, id));
		} else
			Error("Send", "DelayedChanges still 0");
	} else {
		Collection *observers= GetObservers();
		if (observers)
			observers->ForEach(Object,DoObserve)(id, part, vp, this);
	}
}

void Object::DoObserve(int, int, void*, Object*)
{
}

Iterator *Object::GetObserverIter()
{
	if (!IsObserved())
		return 0;
	Collection *observers= GetObservers();
	if (observers)
		return observers->MakeIterator();
	return 0;
}

void Object::DelayChanges()
{
	SeqCollection *col= 0;

	flags_ |= cObjDelayChanges;
	if (DelayedChanges == 0)
		DelayedChanges= new IdDictionary;
	else
		col= (SeqCollection*) DelayedChanges->AtKey(this);
	if (col) {
		ObjInt *ref= (ObjInt*) col->At(0);
		++(*ref);
	} else {
		col= new OrdCollection;
		col->Add(new ObjInt(0));
		DelayedChanges->PutAtKeyIfAbsent(col, this);
	}
}

void Object::FlushChanges()
{
	if (DelayedChanges) {
		SeqCollection *col= (SeqCollection*) DelayedChanges->AtKey(this);
		if (col) {
			ObjInt *ref= (ObjInt*)col->At(0);
			if (ref->GetValue() <= 0) {
				flags_ &= ~cObjDelayChanges;
				ChangeMessage *cm;
				Iter next(col);
				next(); // overread reference count
				while (cm= (ChangeMessage*) next())
					Send(cm->id, cm->part, cm->what);
				DelayedChanges->RemoveKey(this);
				col->FreeAll();
				SafeDelete(col);
			} else
				(*ref)--;
		}
	}
}

bool Object::PrintOnWhenObserving(Object*)
{
	return FALSE;
}

//---- error handling ----------------------------------------------------------

void Object::DoError(int level, const char *location, const char *fmt, va_list va)
{
	::ErrorHandler(level, form("%s::%s", ClassName(), location), fmt, va);
}

void Object::Error(const char *location, const char *va_(fmt), ...)
{
	va_list ap;
	va_start(ap, va_(fmt));
	DoError(cError, location, va_(fmt), ap);
	va_end(ap);
}

void Object::SysError(const char *location, const char *va_(fmt), ...)
{
	va_list ap;
	va_start(ap, va_(fmt));
	DoError(cSysError, location, va_(fmt), ap);
	va_end(ap);
}

void Object::Warning(const char *location, const char *va_(fmt), ...)
{
	va_list ap;
	va_start(ap, va_(fmt));
	DoError(cWarning, location, va_(fmt), ap);
	va_end(ap);
}

void Object::Fatal(const char *location, const char *va_(fmt), ...)
{
	va_list ap;
	va_start(ap, va_(fmt));
	DoError(cFatal, location, va_(fmt), ap);
	va_end(ap);
}

//---- fire walls --------------------------------------------------------------

void Object::AbstractMethod(const char *method)
{
	Warning(method, "abstract method called");
}

void Object::MayNotUse(const char *method)
{
	Warning(method, "not allowed to use this method");
}

Object *Object::guard(Class *should)
{
	if (this && IsA()->isKindOf(should))
		return this;
	if (this)
		Error("Guard", "object is not a %s but a %s", should->Name(), ClassName());
	return 0;
}

//---- inspecting, debugging ---------------------------------------------------

void Object::Inspect(bool block)
{
	gProgEnv->InspectObject(this, block);
}

void Object::EditSource(bool definition)
{
	gProgEnv->EditSourceOf(IsA(), definition);
}

void Object::InspectorId(char *buf, int bufSize)
{
	const char *name= AsString();
	if (name)
		strn0cpy(buf, name, bufSize);
}

void Object::CollectParts(Collection *)
{
}

void Object::ScanMembers(u_long va_(firstTag), ...)
{
	int n= _an;
	_an= 0;
	va_list ap;
	va_start(ap, va_(firstTag));
	if (_accessMembers)
		_accessMembers->ScanMembers(this, (int)va_(firstTag), ap, n);
	va_end(ap);
}

//---- interface for accessing instance variables by name will be improved
//     in a future release

	class FindMember: public AccessMembers {
	public:
		const char *lookfor;
		int offset, len, type;
		Object *op;
		Class *classp;
		
		FindMember(Object *in, const char *name)
			{ offset= -1; op= in; lookfor= name; type= 0; len= -1;}
		void Member(char *name, int offset, int lenOrOffset, int type,
													Class*, bool g, bool lg);
	};
		
	void FindMember::Member(char *name, int o, int, int t, Class *c, bool, bool)
	{
		if (strcmp(name, lookfor) == 0) {
			offset= o;
			type= t;
			classp= c;
			// if (l == -1)
				// len= GetLength(op, o, l, t, c, g, lg);
		}
	}
		
void *Object::GetAddrOf(const char *name, Class *&cl)
{
	FindMember m(this, name);
	IsA()->EnumerateMembers(&m);
	if (m.offset != -1) {
		cl= m.classp;
		return (void*) ((u_long) this + (u_long) m.offset);
	}
	return 0;
}

